#pragma once

#include <CXX/Objects.hxx>
#include <bitset>

#define PY_TRY	try

#define __PY_CATCH(R)                                           \
catch(const Py::Exception&)                                     \
{                                                               \
    R;                                                          \
}                                                               

#  define _PY_CATCH(R)                                              \
    __PY_CATCH(R)

#define PY_CATCH _PY_CATCH(return(nullptr))


#define PyMOD_INIT_FUNC(name) PyMODINIT_FUNC PyInit_##name(void)
#define PyMOD_Return(name) return name

#define Py_Return return Py_INCREF(Py_None), Py_None

#define Py_Header                                           \
public:                                                     \
    static PyTypeObject   Type;                             \
    static PyMethodDef    Methods[];                        \
    virtual PyTypeObject *GetType(void) {return &Type;}


inline int streq(const char* A, const char* B)  // define "streq"
{
	return strcmp(A, B) == 0;
}


inline void Assert(int expr, char* msg)         // C++ assert
{
	if (!expr)
	{
		fprintf(stderr, "%s\n", msg);
		exit(-1);
	};
}

class PyObjectBase :public PyObject
{
    Py_Header
		enum Status {
		Valid = 0,
		Immutable = 1,
		Notify = 2,
		NoTrack = 3
	};
protected:
    virtual ~PyObjectBase();
	void setTwinPointer(void* ptr) {
		_pcTwinPointer = ptr;
	}
public:
    PyObjectBase(void*, PyTypeObject* T);
    static void PyDestructor(PyObject* P) { delete ((PyObjectBase*)P); }
    PyObjectBase* IncRef() { Py_INCREF(this); return this; }
    PyObjectBase* DecRef() { Py_DECREF(this); return this; }
	void* getTwinPointer() const {
		return _pcTwinPointer;
	}
    virtual PyObject* _getattr(const char* attr);
    static  PyObject* __getattro(PyObject* PyObj, PyObject* attro);
    virtual int _setattr(const char* attro, PyObject* value);
    static  int __setattro(PyObject* PyObj, PyObject* attro, PyObject* value);
    virtual PyObject* _repr();
	static  PyObject* __repr(PyObject* PyObj) {
		if (!((PyObjectBase*)PyObj)->isValid()) {
			PyErr_Format(PyExc_ReferenceError, "Cannot print representation of deleted object");
			return nullptr;
		}
		return ((PyObjectBase*)PyObj)->_repr();
	}
	virtual int PyInit(PyObject* /*args*/, PyObject* /*kwd*/)
	{
		return 0;
	}
	static  int __PyInit(PyObject* self, PyObject* args, PyObject* kwd)
	{
		return ((PyObjectBase*)self)->PyInit(args, kwd);
	}
	void setInvalid() {
		StatusBits.reset(Valid);
		clearAttributes();
		_pcTwinPointer = nullptr;
	}
	bool isValid() {
		return StatusBits.test(Valid);
	}
	void setConst() {
		StatusBits.set(Immutable);
	}
	bool isConst() {
		return StatusBits.test(Immutable);
	}

	void setShouldNotify(bool on) {
		StatusBits.set(Notify, on);
	}

	bool shouldNotify() const {
		return StatusBits.test(Notify);
	}

	void startNotify();

	void setNotTracking(bool on = true) {
		StatusBits.set(NoTrack, on);
	}
	bool isNotTracking() const {
		return StatusBits.test(NoTrack);
	}
	typedef void* PointerType;
private:
	void setAttributeOf(const char* attr, PyObject* par);
	void resetAttribute();
	PyObject* getTrackedAttribute(const char* attr);
	void trackAttribute(const char* attr, PyObject* obj);
	void untrackAttribute(const char* attr);
	void clearAttributes();

protected:
	std::bitset<32> StatusBits;
	void* _pcTwinPointer;

public:
	PyObject* baseProxy;
private:
	PyObject* attrDict;
};